package com.loser.gateway.base.impl;

import com.loser.gateway.base.Dispatcher;
import com.loser.gateway.cmd.after.CmdAfterHandler;
import com.loser.gateway.cmd.before.CmdBeforeHandler;
import com.loser.module.base.proto.base.InnerRequest;
import com.loser.module.base.proto.base.InnerResponse;
import com.loser.utils.ContextUtil;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;


@Component
@Slf4j
public class DispatcherImpl implements Dispatcher, ApplicationContextAware {

    private final Map<Integer, CmdAfterHandler> ACTER_CMD_MAP = new ConcurrentHashMap<>();
    private final Map<Integer, CmdBeforeHandler> BEFORE_CMD_MAP = new ConcurrentHashMap<>();

    private RestTemplate restTemplate;

    private LoadBalancerClient loadBalancerClient;

    public LoadBalancerClient getDiscoveryClient() {

        if (Objects.nonNull(loadBalancerClient)) {
            return loadBalancerClient;
        }
        synchronized (DispatcherImpl.class) {
            if (Objects.nonNull(loadBalancerClient)) {
                return loadBalancerClient;
            }
            loadBalancerClient = ContextUtil.getBean(LoadBalancerClient.class);
        }
        return loadBalancerClient;

    }

    public RestTemplate getRestTemplate() {

        if (Objects.nonNull(restTemplate)) {
            return restTemplate;
        }
        synchronized (DispatcherImpl.class) {
            if (Objects.nonNull(restTemplate)) {
                return restTemplate;
            }
            restTemplate = ContextUtil.getBean(RestTemplate.class);
        }
        return restTemplate;

    }

    @Override
    public void process(ChannelHandlerContext ctx, InnerRequest innerRequest) {

        long start = System.currentTimeMillis();

        // 01 处理前置逻辑
        CmdBeforeHandler beforeHandler = BEFORE_CMD_MAP.get(innerRequest.getMethodCmd());
        if (Objects.nonNull(beforeHandler)) {
            beforeHandler.process(ctx, innerRequest);
            if (beforeHandler.isReturn()) {
                return;
            }
        }

        // 02 处理远程调用cmd接口逻辑
        String serviceName = innerRequest.getService();
        ServiceInstance serviceInstance = getDiscoveryClient().choose(serviceName);
        RestTemplate template = getRestTemplate();
        String target = String.format("http://%s:%s/cmd/invoke", serviceInstance.getHost(), serviceInstance.getPort());
        InnerResponse resp = template.postForObject(target, innerRequest, InnerResponse.class);

        // 03 处理后置逻辑
        CmdAfterHandler cmdHandler = ACTER_CMD_MAP.get(innerRequest.getMethodCmd());
        if (Objects.nonNull(cmdHandler)) {
            cmdHandler.process(ctx, innerRequest);
        }

        long end = System.currentTimeMillis();
        log.error("process cost {}", end - start);
        ctx.writeAndFlush(resp);

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        List<CmdAfterHandler> beans = ContextUtil.getBeans(applicationContext, CmdAfterHandler.class);
        for (CmdAfterHandler handler : beans) {
            ACTER_CMD_MAP.put(handler.getCmd(), handler);
            log.info("put afterCmdHandler cmd {} bean {}", handler.getCmd(), handler.getClass().getName());
        }

        List<CmdBeforeHandler> beforeHandlers = ContextUtil.getBeans(applicationContext, CmdBeforeHandler.class);
        for (CmdBeforeHandler handler : beforeHandlers) {
            BEFORE_CMD_MAP.put(handler.getCmd(), handler);
            log.info("put beforeCmdHandler cmd {} bean {}", handler.getCmd(), handler.getClass().getName());
        }


    }
}
